package com.biboheart.adapter.compiler.grammar;

import com.biboheart.adapter.compiler.ast.Node;
import com.biboheart.adapter.compiler.enums.NodeType;
import com.biboheart.adapter.compiler.enums.TokenType;
import com.biboheart.adapter.compiler.token.Token;
import com.biboheart.adapter.compiler.utils.TokenUtils;
import com.biboheart.adapter.support.enums.DelimitEnum;

import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

public class CommandExpression implements Expression{
    private final List<Token> tokenList;
    private final Stack<Token> stack = new Stack<>();

    public CommandExpression(List<Token> tokenList) {
        this.tokenList = tokenList;
    }

    @Override
    public Node parser() {
        if (null == tokenList) {
            return null;
        }
        Token token = tokenList.stream().findFirst().orElse(null);
        if (null == token) {
            return null;
        }
        Node node = new Node();
        node.setToken(token);
        node.setExpression(null);
        node.setNodeType(NodeType.NODE_COMMAND);
        List<Node> children = new ArrayList<>();
        node.setChildren(children);
        // 指令入参之间用","分隔，根据","将参数分解成子节点
        List<Token> expr = new ArrayList<>();
        for (int i = 1; i < tokenList.size(); i ++) {
            token = tokenList.get(i);
            if (DelimitEnum.DELIMIT_SLP.code().equals(token.getCode())) {
                stack.push(token);
                if (stack.size() > 1) {
                    expr.add(token);
                }
                continue;
            }
            if (DelimitEnum.DELIMIT_SRP.code().equals(token.getCode())) {
                stack.pop();
                if (!stack.isEmpty()) {
                    expr.add(token);
                }
                continue;
            }
            if (DelimitEnum.DELIMIT_COM.code().equals(token.getCode())) {
                if (stack.size() > 1) {
                    expr.add(token);
                } else {
                    Node child = new Node();
                    if (expr.isEmpty()) {
                        child.setNodeType(NodeType.NODE_CONSTANT);
                        child.setToken(null);
                    } else if (expr.size() == 1) {
                        child.setNodeType(TokenUtils.compareNodeType(expr));
                        child.setToken(expr.get(0));
                    } else {
                        child.setNodeType(TokenUtils.compareNodeType(expr));
                        child.setExpression(expr);
                    }
                    children.add(child);
                    expr = new ArrayList<>();
                }
                continue;
            }
            expr.add(token);
        }
        Node child = new Node();
        if (expr.isEmpty()) {
            child.setNodeType(NodeType.NODE_CONSTANT);
            child.setToken(null);
        } else if (expr.size() == 1) {
            child.setNodeType(TokenUtils.compareNodeType(expr));
            child.setToken(expr.get(0));
        } else {
            child.setNodeType(TokenUtils.compareNodeType(expr));
            child.setExpression(expr);
        }
        children.add(child);
        return node;
    }

    private NodeType compareNodeType(List<Token> expr) {
        Token token = expr.get(0);
        if (expr.size() > 1 && (TokenType.TOKEN_VARIABLE.equals(token.getType())
                || TokenType.TOKEN_CONSTANT.equals(token.getType())
                || TokenType.TOKEN_STRING.equals(token.getType())
                || TokenType.TOKEN_NUMBER.equals(token.getType())
                || TokenType.TOKEN_NULL.equals(token.getType())
        )) {
            token = expr.get(1);
        }
        NodeType nodeType = null;
        if (TokenType.TOKEN_KEYWORD.equals(token.getType())) {
            nodeType = NodeType.NODE_COMMAND;
        } else if (TokenType.TOKEN_VARIABLE.equals(token.getType())) {
            nodeType = NodeType.NODE_VARIABLE;
        } else if (TokenType.TOKEN_CONSTANT.equals(token.getType())
                || TokenType.TOKEN_STRING.equals(token.getType())
                || TokenType.TOKEN_NUMBER.equals(token.getType())
                || TokenType.TOKEN_NULL.equals(token.getType())
        ) {
            nodeType = NodeType.NODE_CONSTANT;
        } else if (TokenType.TOKEN_LOGICAL.equals(token.getType()) || TokenType.TOKEN_OPERATOR.equals(token.getType())) {
            nodeType = NodeType.NODE_OPERATOR;
        }
        return nodeType;
    }
}
